<template>
  <el-dialog v-model="visible" class="custom-transition-dialog" :title="title" :width="width"
    :transition="transitionConfig" :close-on-click-modal="false" @closed="handleClosed">
    <!-- 表单 -->
    <el-form ref="formRef" :model="internalForm" :rules="rules" label-width="100px">
      <!-- 插槽：外部传入表单项 -->
      <slot />
    </el-form>
    <template #footer>
      <el-button @click="handleCancel">{{ cancelText }}</el-button>
      <el-button type="primary" @click="handleSubmit">
        {{ confirmText }}
      </el-button>
    </template>
  </el-dialog>
</template>

<script lang="ts" setup>
import { computed, ref, watch } from 'vue'

import type { DialogTransition } from 'element-plus'

/** 接收 props */
const props = defineProps({
  title: {
    type: String,
    default: ''
  },
  formModel: {
    type: Object,
    required: true
  },
  width: {
    type: String,
    default: '30%'
  },
  rules: {
    type: Object,
    default: () => ({})
  },
  cancelText: {
    type: String,
    default: '取消'
  },
  confirmText: {
    type: String,
    default: '确定'
  }
})

/** 事件 */
const emit = defineEmits(['submit', 'cancel'])

/** 内部状态 */
const visible = ref(false)
const formRef = ref(null)
const currentAnimation = ref('slide') // 默认动画
const isObjectConfig = ref(false)

const internalForm = ref({})


const transitionConfig = computed<DialogTransition>(() => {
  if (isObjectConfig.value) {
    return {
      name: 'dialog-custom-object',
      appear: true,
      mode: 'out-in',
      duration: 500,
    }
  }
  return `dialog-${currentAnimation.value}`
})

/** 打开弹窗 */
const open = (type = 'scale', objectConfig = false) => {
  currentAnimation.value = type
  isObjectConfig.value = objectConfig
  visible.value = true
}

/** 关闭弹窗 */
const handleCancel = () => {
  visible.value = false
  emit('cancel')
}

/** 提交表单 */
const handleSubmit = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      emit('submit', JSON.parse(JSON.stringify(internalForm.value)))
    }
  })
}

/** 弹窗关闭后，清空表单验证状态 */
const handleClosed = () => {
  if (formRef.value) {
    formRef.value.resetFields()    // 重置字段
    formRef.value.clearValidate()  // 清空校验
  }
}

watch(
  () => props.formModel,
  (val) => {
    internalForm.value = { ...val } // 拷贝一份，不直接修改 props
  },
  { immediate: true, deep: true }
)

/** 暴露方法 */
defineExpose({
  open,
  close: handleCancel
})
</script>

<style scoped>
code {
  background: var(--el-bg-color-page);
  padding: 4px 8px;
  border-radius: 4px;
  font-size: 12px;
  display: block;
  margin-top: 8px;
}
</style>

<style>
/* Scale Animation */
.dialog-scale-enter-active,
.dialog-scale-leave-active,
.dialog-scale-enter-active .el-dialog,
.dialog-scale-leave-active .el-dialog {
  transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}

.dialog-scale-enter-from,
.dialog-scale-leave-to {
  opacity: 0;
}

.dialog-scale-enter-from .el-dialog,
.dialog-scale-leave-to .el-dialog {
  transform: scale(0.5);
  opacity: 0;
}

/* Slide Animation */
.dialog-slide-enter-active,
.dialog-slide-leave-active,
.dialog-slide-enter-active .el-dialog,
.dialog-slide-leave-active .el-dialog {
  transition: all 0.3s cubic-bezier(0.25, 0.46, 0.45, 0.94);
}

.dialog-slide-enter-from,
.dialog-slide-leave-to {
  opacity: 0;
}

.dialog-slide-enter-from .el-dialog,
.dialog-slide-leave-to .el-dialog {
  transform: translateY(-100px);
  opacity: 0;
}

/* Bounce Animation */
.dialog-bounce-enter-active,
.dialog-bounce-leave-active,
.dialog-bounce-enter-active .el-dialog,
.dialog-bounce-leave-active .el-dialog {
  transition: all 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
}

.dialog-bounce-enter-from,
.dialog-bounce-leave-to {
  opacity: 0;
}

.dialog-bounce-enter-from .el-dialog,
.dialog-bounce-leave-to .el-dialog {
  transform: scale(0.3) translateY(-50px);
  opacity: 0;
}

/* Object Configuration Animation */
.dialog-custom-object-enter-active,
.dialog-custom-object-leave-active,
.dialog-custom-object-enter-active .el-dialog,
.dialog-custom-object-leave-active .el-dialog {
  transition: all 0.5s cubic-bezier(0.25, 0.8, 0.25, 1);
}

.dialog-custom-object-enter-from,
.dialog-custom-object-leave-to {
  opacity: 0;
}

.dialog-custom-object-enter-from .el-dialog,
.dialog-custom-object-leave-to .el-dialog {
  transform: rotate(180deg) scale(0.5);
  opacity: 0;
}
</style>